/*
 * surface.cpp
 *
 *  Created on: Dec 9, 2008
 *      Author: yangcheng
 */
#include"surface.h"
#include<iostream>
using namespace std;
WingedEdge* readGLM(GLMmodel *model) {
	GLuint numVertices;
	GLuint numTriangles;

	Vertex** va = new Vertex*[model->numvertices + 1];
	Triangle** ta = new Triangle*[model->numtriangles];
	Edge** ea = new Edge*[model->numtriangles * 3];

	WingedEdge* we;//to be returned
	Face *fprev, *fcurr;
	Vertex *vprev, *vcurr;

	for (int i = 1; i <= model->numvertices; i++) {
		va[i]
				= new Vertex(model->vertices[i * 3], model->vertices[i * 3 + 1], model->vertices[i
						* 3 + 2]);
	}
	//node the index differs! obj's vertex index start from 1
	//colors are not considered
	for (int i = 0; i < model->numtriangles; i++) {
		ta[i]
				= new Triangle(model->triangles[i].vindices[0], model->triangles[i].vindices[1], model->triangles[i].vindices[2]);
	}

	we = new WingedEdge;
	we->faces = new Face;
	fprev = fcurr = we->faces;

	//generate the edge list
	for (int i = 0; i < model->numtriangles; i++) {
		fprev->next = fcurr;
		for (int j = 0; j < 3; j++) {
			int c = i * 3 + j;
			ea[c] = new Edge;
			ea[c]->head = va[ta[i]->verts[j % 3]];
			ea[c]->tail = va[ta[i]->verts[(j + 1) % 3]];
			ea[c]->left = fcurr;
		}
		fcurr->edges = ea[3 * i];//the triangle point to one edge

		for (int j = 0; j < 3; j++) {//fill the linked list to a circle list
			ea[3 * i + j]->prev = ea[3 * i + (j + 1) % 3];
			ea[3 * i + j]->next = ea[3 * i + (j + 2) % 3];
		}

		fprev = fcurr;
		fcurr = new Face;
	}

	for (int i = 0; i < 3 * model->numtriangles; i++) {
		if (ea[i]->twin == NULL) {

			for (int j = 0; j < 3 * model->numtriangles; j++) {
				if (ea[j]->twin == NULL) {
					if (ea[j]->head == ea[i]->tail && ea[i]->head
							== ea[j]->tail) {
						ea[i]->twin = ea[j];
						ea[j]->twin = ea[i];
						//		ea[i]->right = ea[j]->left;
						//	ea[j]->right = ea[i]->left;
					}
				}
			}
		}
	}

	vprev = we->vertices = va[1];
	for (int i = 2; i <= model->numvertices; i++) {
		vcurr = va[i];
		vprev->next = vcurr;
		vprev = vcurr;
	}
	delete[] ea;
	delete[] va;
	delete[] ta;
	return we;
}

GLvoid generateNormals(WingedEdge* we) {
	Face *face;
	face = we->faces;
	while (face) {

		Vertex v1(face->edges->tail->x - face->edges->head->x,
				face->edges->tail->y - face->edges->head->y,
				face->edges->tail->z - face->edges->head->z);
		Vertex v2(face->edges->prev->tail->x - face->edges->prev->head->x,
				face->edges->prev->tail->y - face->edges->prev->head->y,
				face->edges->prev->tail->z - face->edges->prev->head->z);
		face->normal = crossProduct(v2, v1);
		normalize(*(face->normal));
		face = face->next;
	}
}
void wireWe(WingedEdge* we) {
	Face* face;
	Edge* edge;
	Edge* firstEdge;
	long num = 0;

	face = we->faces;

	while (face) {
		firstEdge = edge = face->edges;
		glColor4f(1.0, 0.0, 0.0, 0.0);
		glBegin(GL_LINE_LOOP);
		glNormal3f(face->normal->x, face->normal->y, face->normal->z);

		do {

			glVertex3f(edge->head->x, edge->head->y, edge->head->z);
			edge = edge->next;
		} while (firstEdge != edge);

		glEnd();
		face = face->next;
		num++;
	}

}

void flatWe(WingedEdge* we) {
	Face* face;
	Edge* edge;
	Edge* firstEdge;

	face = we->faces;
	while (face) {

		firstEdge = edge = face->edges;
		glColor4f(1.0, 0.0, 0.0, 0.0);

		glBegin(GL_TRIANGLES);
		glNormal3f(face->normal->x, face->normal->y, face->normal->z);

		do {
			glVertex3f(edge->head->x, edge->head->y, edge->head->z);
			edge = edge->next;
		} while (firstEdge != edge);

		glEnd();

		face = face->next;
	}
}

GLvoid subDivision(WingedEdge *we) {
	Vertex* vertex;
	vertex = we->vertices;
	while (vertex) {
		vertex->newpoint = GL_FALSE;
		delete vertex->averaged;
		vertex->averaged = NULL;
		vertex = vertex->next;
	}
	generateMid(we->faces);
	generateOdd(we);
	connectEdge(we->faces);
	printf("finished step 1");
}

/*
 *step one: find the mid-point of every edge, and connect them to form new edges/faces
 * */

GLvoid generateMid(Face * face) {
	Edge* edge;
	Vertex* vertex;
	Vertex* m;
	Edge* newedge;
	Edge* twinedge;

	edge = face->edges;

	do {
		assert(edge);
		assert(edge->next);
		assert(edge->next->twin);
		assert(edge->next->twin->next);

		if (edge->next->twin->next->twin != edge) { /* There is work here to be done! */
			/* get the midpoint */
			m = midpoint(edge->head, edge->tail);
			m->newpoint = GL_TRUE; /* flag this one as new! */

			/* insert the new vertex into the list */
			vertex = edge->tail->next;
			edge->tail->next = m;
			m->next = vertex;

			/* create new edge */
			newedge = new Edge;
			newedge->head = edge->head;
			newedge->tail = m;
			newedge->prev = edge;
			newedge->next = edge->next;
			edge->next->prev = newedge;
			edge->next = newedge;
			newedge->twin = edge->twin;
			edge->head = m;
			edge->twin->twin = newedge;

			/* create a new edge for the twin */
			twinedge = new Edge;
			twinedge->head = edge->twin->head;
			twinedge->tail = m;
			twinedge->prev = edge->twin;
			twinedge->next = edge->twin->next;
			edge->twin->next->prev = twinedge;
			edge->twin->next = twinedge;
			twinedge->twin = edge;
			edge->twin->head = m;
			edge->twin = twinedge;

			generateMid(edge->next->twin->left);
		}
		edge = edge->next->next;
	} while (edge != face->edges);
}

/*update the odd vertices, newpoint are consider to be odd
 * */
GLvoid generateOdd(WingedEdge *we) {

	Vertex* vertex, *avg;
	Face* face;
	Edge* edge;

	vertex = we->vertices;

	for (face = we->faces; face; face = face->next) {
		edge = face->edges;
		do {
			if (edge->head->averaged == NULL && edge->head->newpoint == GL_TRUE) {
				edge->head->averaged = new Vertex(0.0, 0.0, 0.0);
				avg=edge->head->averaged;
				avg->x += edge->next->head->x * (3 / 8.0)
				+ edge->tail->x * (3
						/ 8.0) + edge->next->next->next->head->x * (1 / 8.0)
						+ edge->twin->next->next->next->head->x * (1 / 8.0);
				avg->y += edge->next->head->y * (3 / 8.0) + edge->tail->y * (3
						/ 8.0) + edge->next->next->next->head->y * (1 / 8.0)
						+ edge->twin->next->next->next->head->y * (1 / 8.0);
				avg->z += edge->next->head->z * (3 / 8.0) + edge->tail->z * (3
						/ 8.0) + edge->next->next->next->head->z * (1 / 8.0)
						+ edge->twin->next->next->next->head->z * (1 / 8.0);

			}
			edge = edge->next->next;
		} while (edge != face->edges);
	}

}

GLvoid connectEdge(Face* faces) {
	Edge* edge;
	Edge* newedge;
	Edge* twinedge;
	Face* face;
	Face* newface;
	Face* faceptr;

	face = faces;

	/* first let's add some new edges */
	while (face) {

		edge = face->edges;

		do {
			newedge = new Edge;

			newedge->head = edge->prev->tail;
			newedge->tail = edge->head;
			newedge->next = edge->prev;
			newedge->prev = edge;
			newedge->left = NULL;

			/* this is how we'll keep track of the new edges */
			edge->prev->prev = newedge;

			twinedge = new Edge;
			twinedge->head = newedge->tail;
			twinedge->tail = newedge->head;
			twinedge->twin = newedge;
			newedge->twin = twinedge;
			twinedge->left = NULL;

			edge = edge->next->next;
		} while (edge != face->edges);
		face = face->next;
	}

	face = faces;
	while (face) {
		if (face->edges->right != face->edges->left) { /* make sure to skip the new faces */
			/* add the 3 corner faces (actually, we reuse one) */
			newedge = edge = face->edges;
			do {
				if (edge != newedge) { /* is this really a new face? */
					newface = new Face;
					/* add the new face to the list */
					faceptr = face->next;
					face->next = newface;
					newface->next = faceptr;
				} else {
					newface = face;
				}
				/* tell all the edges about this new face */
				edge->left = newface;
				edge->prev->left = newface;
				edge->prev->prev->left = newface;

				/* assign an edge to the new face */
				assert(newface);
				newface->edges = edge;
				edge->right = edge->left; /* mark this face as new! */
				edge = edge->next->next;
			} while (edge != newedge);

			edge = face->edges;

			edge->right = edge->left;

			/* add the inner face */
			newface = new Face;

			/* add the new face to the list */
			faceptr = face->next;
			face->next = newface;
			newface->next = faceptr;

			/* assign an edge to the new face */
			newface->edges = edge->prev->prev->twin;

			/* complete the inner edges */
			do {
				newedge = edge->prev->prev->twin;
				newedge->next = edge->next->prev->twin;
				newedge->next->prev = newedge;
				/* tell all the edges about this new face */
				newedge->left = newface;
				newedge->right = newedge->left;
				edge = edge->next->next;
			} while (edge != face->edges);
			/* complete the outer edges */
			edge = face->edges;
			do {
				edge->next = edge->prev->prev;
				edge = edge->next->twin->next->twin->prev; /* phew! */
			} while (edge != face->edges);
			edge = face->edges;
			do {
				edge->prev->prev = edge->next;
				edge = edge->next->twin->next->twin->prev; /* phew! */
			} while (edge != face->edges);
		}
		face = face->next;
	}
	face = faces;
	while (face) { /* now to add those right faces */
		edge = face->edges;
		do {
			edge->right = edge->twin->left;
			edge = edge->next;
		} while (edge != face->edges);
		face = face->next;
	}
}


GLvoid maskOdd(WingedEdge *we){
	Vertex* vertex;
	for(vertex=we->vertices;vertex;vertex=vertex->next)
	{
		if(vertex->newpoint==GL_TRUE)
		{
			vertex->x=vertex->averaged->x;
			vertex->y=vertex->averaged->y;
			vertex->z=vertex->averaged->z;
		}
	}
}
//update the even vertieces.
GLvoid maskEven(WingedEdge *we) {
	Vertex* vertex;
	Vertex* avg;
	Face* face;
	Edge* edge;
	Edge* inneredge;
	Edge* firstEdge;
	GLuint n;

	vertex = we->vertices;
	face = we->faces;

	while (face) {
		edge = face->edges;

		do {
			if (edge->head->averaged == NULL
					//&& edge->head->newpoint== GL_FALSE
					) {
				inneredge = edge;
				n = 0;
				edge->head->averaged = new Vertex(0.0, 0.0, 0.0);
				avg = edge->head->averaged;
				avg->x = avg->y = avg->z = 0.0;
				do {
					avg->x += inneredge->tail->x;
					avg->y += inneredge->tail->y;
					avg->z += inneredge->tail->z;
					inneredge = inneredge->next->twin;
					n++;
				} while (inneredge != edge);
				float b = beta(n);
				avg->x = avg->x * (b / n) + edge->head->x * (1 - b);
				avg->y = avg->y * (b / n) + edge->head->y * (1 - b);
				avg->z = avg->z * (b / n) + edge->head->z * (1 - b);
			}
			edge = edge->next;
		} while (edge != face->edges);
		face = face->next;
	}

	for (vertex = we->vertices; vertex; vertex = vertex->next) {
		assert(vertex->averaged);
		vertex->x = vertex->averaged->x;
		vertex->y = vertex->averaged->y;
		vertex->z = vertex->averaged->z;


	}

}

